<?php

namespace app\index\controller;

use app\common\model\ExportLog;
use app\common\model\Question;
use think\Request;
use app\index\BaseController;
use RuntimeException;
use ZipArchive;

class Export extends BaseController
{
    public static function buildZipPath($code)
    {
        return env('root_path') . 'runtime/zip/ark.' . $code . '.zip';
    }

    /**
     * 获取所有导出记录
     *
     * @return \think\Response
     */
    public function getAllLogs()
    {
        if (request()->type() === 'json') {
            return json(
                ExportLog::where(1)->select()
            );
        } else {
            // 允许从浏览器访问直接导出
            $html = <<<EOF
<form action="/api/v1/export" method="post">
    抽取类型，1随机，2顺序，必选<br>
    <input type="text" name="extract_type" value="1"><br>
    导出类型，1为智力方舟格式，2为抢答题+智力方舟格式，类型2时强制非图片题<br>
    <input type="text" name="output_type" value="2"><br>
    抽取题目总数，必选<br>
    <input type="text" name="total" value="250"><br>
    导出注释，用于方便自己查看，建议不要留空<br>
    <input type="text" name="comment" value="导出题目"><br>
    题目形式，0选择，1填空，null则忽略<br>
    <input type="text" name="style" value=""><br>
    是否由图片题，null则忽略<br>
    <input type="text" name="has_image" value=""><br>
    开始位置，默认为0<br>
    <input type="text" name="offset" value=""><br>
    <input type="submit" value="OK">
</form>
EOF;
            return response($html);
        }
    }

    /**
     * 抽取题目
     *
     * @return \think\Response
     * @args extract_type 抽取类型，1随机，2顺序，必选
     * @args output_type 导出类型，1为智力方舟格式，2为抢答题+智力方舟格式，类型2时强制非图片题
     * @args total 抽取题目总数，必选
     * @args comment+ 导出注释，用于方便自己查看，建议不要留空
     * @args style+ 题目形式，0选择，1填空，null则忽略
     * @args has_image+ 是否由图片题，null则忽略
     * @args offset+ 开始位置，默认为0
     */
    public function extractQuestions()
    {
        $extract_type = input('extract_type');
        $output_type = input('output_type');
        $total = input('total');
        $comment = input('comment', '');
        $style = input('style');
        $has_image = input('has_image');
        $offset = input('offset', 0);
        $extract_type = intval($extract_type);
        $output_type = intval($output_type);

        if (!in_array($extract_type, [1,2])) {
            error('不识别的抽取类型' . $extract_type);
        }
        if (!in_array($output_type, [1,2])) {
            error('不识别的导出类型' . $output_type);
        }

        // 整理参数
        if (!$total) {
            error('必须指定抽取题目总数');
        }
        if (is_numeric($style)) {
            $style = intval($style);
        } else {
            $style = null;
        }

        // 调整参数
        if ($extract_type === 1) {
            $offset = 0;
        }
        if ($output_type === 2) {
            $has_image = false;
        }

        // 设定通用参数
        $query = Question::where('is_exported', 0)->where('status', Questions::STAUTS_PASS);
        if ($style !== null) {
            $query->where('style', $style);
        }
        if ($has_image === true) {
            $query->whereNotNull('image');
        } else if ($has_image === false) {
            $query->whereNull('image');
        }

        // 按特定参数查询题目，并排序
        if ($extract_type === 1) {
            $query->orderRaw('RAND()');
            $query->limit(0, $total);

            $questions = $query->select()->sort(function ($a, $b) {
                return $a->id - $b->id;
            });
        } else if ($extract_type === 2) {
            $query->limit($offset, $total);;

            $questions = $query->select();
        }

        /** @var \think\Collection $questions */

        $questionsCount = $questions->count();
        if ($questionsCount <= 0) {
            error('没有符合条件的题目，无法导出');
        }

        // 检查导出路径是否存在
        $zipCode = rand(10000, 99999);
        $zipPath = self::buildZipPath($zipCode);
        if (file_exists($zipPath)) {
            error('文件已存在，运气不好，请重试一次');
        }

        // 开始导出
        ignore_user_abort(true); // 即使Client断开(如关掉浏览器)，PHP脚本也可以继续执行.
        set_time_limit(0); // 执行时间为无限制，php默认执行时间是30秒，可以让程序无限制的执行下去

        // 模型更新
        $db = $questions[0]->db(false);
        $db->startTrans();

        try {
            // 导出内容
            $ark = '';
            $qrq = '';
            foreach ($questions as $item) {
                /** @var Question $item */
                $ark .= $item->buildArkDescription();
                if ($output_type === 2) {
                    $qrq .= $item->buildQRQDescription();
                }
                $item->is_exported = true;
                $item->save();
            }

            // 打包成ZIP
            $zip = new ZipArchive();
            $res = $zip->open($zipPath, ZipArchive::CREATE);
            if ($res === true) {
                $zip->addFromString('ark.txt', $ark);
                $zip->addFromString('qrq.txt', $qrq);
                $zip->close();
            } else {
                throw new RuntimeException('压缩打包时出错');
            }

            // 记录LOG
            $log = new ExportLog();
            $log->comment = $comment;
            $log->ids = array_values($questions->map(function ($item) {
                /** @var Question $item */
                return $item->id;
            })->sort(function ($a, $b) {
                return $a - $b;
            })->toArray());
            $log->zip_code = $zipCode;
            $log->save();

            // 提交
            $db->commit();

            $url = url('index/Export/downloadZip', ['code' => $zipCode], false);
        } catch (\Exception $e) {
            $db->rollback();
            error('导出时发生错误：' . $e->getMessage());
        }

        if (request()->type() === 'json') {
            return json([
                'count' => $questionsCount,
                'url' => $url,
            ]);
        } else {
            return redirect($url);
        }
    }

    /**
     * 下载ZIP文件，请不要使用接口调用，请在新页面打开
     *
     * @return \think\response\Download
     * @args code ZIP编号
     */
    public function downloadZip($code)
    {
        $filePath = self::buildZipPath($code);
        if (!file_exists($filePath)) {
            error('文件不存在，无法下载');
        }

        return download($filePath, "ark.$code.zip");
    }

    /**
     * 撤销某次导出，将题目标记撤回到未导出状态
     *
     * @return \think\Response
     * @args id 记录号ID
     */
    public function withdrawExtract($id)
    {
        // 获取导出记录
        /** @var ExportLog $log */
        $log = ExportLog::where('id', $id)->find();
        if (!$log) {
            error('导出记录不存在');
        }
        $ids = $log->ids;
        $zipCode = $log->zip_code;

        // 开始撤销
        ignore_user_abort(true); // 即使Client断开(如关掉浏览器)，PHP脚本也可以继续执行.
        set_time_limit(0); // 执行时间为无限制，php默认执行时间是30秒，可以让程序无限制的执行下去

        // 开启事务模式
        $db = $log->db(false);
        $db->startTrans();

        try {
            $undraw = [];
            // 逐个撤销
            foreach ($ids as $id) {
                /** @var Question $question */
                $question = Question::where('id', $id)->find();
                if (!$question) {
                    $undraw[] = $id;
                    continue;
                }
                $question->is_exported = false;
                $question->save();
            }

            if (count($undraw) > 0) {
                throw new RuntimeException('以下ID的题目不存在，无法撤销：' . implode(',', $undraw));
            }

            // 删除LOG
            $log->delete();

            // 提交
            $db->commit();
        } catch (\Exception $e) {
            $db->rollback();
            error('导出时发生错误：' . $e->getMessage());
        }

        // 尝试删掉ZIP文件
        $filePath = self::buildZipPath($zipCode);
        if (file_exists($filePath)) {
            @unlink($filePath);
        }

        return json([
            'message' => '成功',
        ]);
    }
}
